#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the version 3 of the GNU Lesser General Public License
#   as published by the Free Software Foundation.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# Copyright (c) NEC Deutschland GmbH, NEC HPC Europe
#
# $Id$

import os
import unittest

from time import time, strftime, localtime, sleep, strptime, mktime, tzset
from rrd import RRD, NS_IN_S
from rra import RRA
from shutil import rmtree


METRICDB = "/tmp/aggmon/metrics"
GROUP_PATH = "grpdata_universe/localhost"
METRICNAME = "Test"
DATABASE_TYPE_FILENAME = "__time_series_database_type__"
NUM_OF_DAYS = 31
Num_OF_HOURS_PER_DAY = 24
NUM_OF_WEEKS = 5

os.environ['TZ'] = 'UTC+0'
tzset()


class RRDTest(unittest.TestCase):
    def setUp( self ):
        f = os.path.join( METRICDB, GROUP_PATH, METRICNAME )
        if os.path.exists( f ):
            rmtree( f )
        os.makedirs( f )
        self.rrd = RRD( f, 30, 24 * 3600 )
        start_s = mktime( strptime( "01 Mar 11 00:00:00", "%d %b %y %H:%M:%S" ) )
        for day in range( 0, NUM_OF_DAYS ):
            for hour in range( 0, Num_OF_HOURS_PER_DAY ):
                time_s = start_s + day * 24 * 3600 + hour * 3600
                value = day + 1
                self.rrd.append( long( time_s * NS_IN_S ), value )

    def tearDown( self ):
        self.rrd.clear()
        pass


class RRDFetchAllTest(RRDTest):
    def runTest( self ):
        records = self.rrd.fetch_all()
        expect = NUM_OF_DAYS * Num_OF_HOURS_PER_DAY   #744 records
        assert len( records ) == expect, "retrieved %d records, expected %d!" % (len( records ), expect )


class RRDFetchFromTest(RRDTest):
    def runTest( self ):
        start_s = mktime( strptime( "25 Mar 11 23:59:59", "%d %b %y %H:%M:%S" ) )
        records = self.rrd.fetch_from( start_s * NS_IN_S )
        expect = (31 - 25 ) * Num_OF_HOURS_PER_DAY
        assert len( records ) == expect, "retrieved %d records, expected %d!" % (len( records ), expect )


class RRDFetchRangeTest(RRDTest):
    def runTest( self ):
        start_s = mktime( strptime( "20 Mar 11 00:00:01", "%d %b %y %H:%M:%S" ) )
        end_s = mktime( strptime( "25 Mar 11 00:00:00", "%d %b %y %H:%M:%S" ) )
        records = self.rrd.fetch_range( start_s * NS_IN_S, end_s * NS_IN_S )
        expect = (25 - 20) * Num_OF_HOURS_PER_DAY
        assert len( records ) == expect, "retrieved %d records, expected %d!" % (len( records ), expect )


class RRDFetchUntilTest(RRDTest):
    def runTest( self ):
        end_s = mktime( strptime( "4 Mar 11 23:59:59", "%d %b %y %H:%M:%S" ) )
        records = self.rrd.fetch_until( end_s * NS_IN_S )
        expect = 4 * Num_OF_HOURS_PER_DAY
        assert len( records ) == expect, "retrieved %d records, expected %d!" % (len( records ), expect )


class RRDInsertTest(RRDTest):
    def runTest( self ):
        f_times = []
        for n in range(0, 3, 1):
            time_s = long( time() )
            f_times.append( ( time_s, strftime( "%Y%m%d%H%M%S", localtime( time_s ) ) ) )
            self.rrd.append( time_s * NS_IN_S, n )
            sleep(2)
        # at this point 3 RRD files are created containing 0.0, 1,0, ...
        #for f in glob.glob( "*.rrd" ):
        #    print f, ", size: ", os.path.getsize(f)
        # insert into each RRD file an additional value, at time-offset 1 second
        for n in range(0, 3, 1):
            insert_time_ns = long( ((f_times[n])[0] + 1) * NS_IN_S )
            self.rrd.insert( insert_time_ns, n + 0.5 )
        #for f in glob.glob( "*.rrd" ):
        #    print f, ", size: ", os.path.getsize(f)
        # retrieve all records and check the inserted ones
        current_time_ns = long( time() * NS_IN_S )
        records = self.rrd.fetch_all( 0, current_time_ns )
        assert records[1].value == 0.5
        assert records[3].value == 1.5
        assert records[5].value == 2.5
        # retrieve a range of values
        start_time_ns = long( (f_times[1])[0] * NS_IN_S )
        end_time_ns = long( (f_times[2])[0] * NS_IN_S )
        records = self.rrd.fetch_range( start_time_ns, end_time_ns )
        assert records[0].time_ns >= start_time_ns
        assert records[1].time_ns <= end_time_ns
        assert records[0].value == 1.0
        assert records[1].value == 1.5


class RRDReprTest(RRDTest):
    # with current configuration this test is not possible since
    # Python only allows 256 function arguments
    def runTest( self ):
        print repr( self.rrd )
        copy_rrd = eval( repr( self.rrd ) )
        print repr( copy_rrd )


class RRDCondenseTest(RRDTest):
    def runTest( self ):
        #os.system( "find " + METRICDB  + " -ls" )
        start_s = mktime( strptime( "01 Apr 11 00:00:00", "%d %b %y %H:%M:%S" ) )
        for day in range( 0, 2 ):
            for hour in range( 0, Num_OF_HOURS_PER_DAY ):
                #print "append day", day, "hour", hour
                time_s = start_s + day * 24 * 3600 + hour * 3600
                value = day + 1
                self.rrd.append( long( time_s * NS_IN_S ), value )
            sleep( 2 )   # do not run daemon as long as the older one is active
        #os.system( "find " + METRICDB  + " -ls" )
        rra = RRA( self.rrd._dir_path )
        records = rra.fetch_all()
        expect = 2 * 23   # daemon run 2 times and created 23 records in RRA files (resulting from 24 records in RRD)
        assert len( records ) == expect, "retrieved %d records, expected %d!" % (len( records ), expect )
        #os.system("cat " + os.path.join( self.rrd._dir_path, "daemon.log" ) )   # dump daemon log


suite = unittest.TestSuite()
suite.addTest( RRDFetchAllTest() )
suite.addTest( RRDFetchFromTest() )
suite.addTest( RRDFetchRangeTest() )
suite.addTest( RRDFetchUntilTest() )
#suite.addTest( RRDInsertTest() )
#suite.addTest( RRDReprTest() )
suite.addTest( RRDCondenseTest() )


if __name__ == "__main__":
    if os.path.exists( METRICDB ):
        print "Path exists!", METRICDB
        #sys.exit( 1 )
        rmtree( METRICDB )
    unittest.TextTestRunner().run( suite )
